Cloudflare Worker 搭建 Telegram 双向私聊机器人

Cloudflare Worker 搭建 Telegram 双向私聊机器人


  1. uuidgenerato获取一个随机uuid作为secret

  2. @get_id_bot获取你的用户id

  3. 登录cloudflare,左侧菜单点击 Workers和 Pages,创建一个worker

  4. 创建 -》 创建 Worker -》输入名称 telegrambot

  5. 配置worker的变量,在设置-》变量-》添加变量
    增加一个BOT_TOKEN变量,值为向官方 bot申请的Bot API Token
    增加一个BOT_SECRET变量,数值为从步骤1中获得的secret
    增加一个MY_ADMIN_UID变量,数值为从步骤2中获得的用户id

  6. 创建一个命名空间为telegrambot的KV数据库

  7. 绑定KV数据库,在设置 -> 变量,下滑找到KV 命名空间绑定:telegrambot -> telegrambot

  8. 编辑代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
const TOKEN = BOT_TOKEN;
const WEBHOOK = '/endpoint';
const SECRET = BOT_SECRET;
const ADMIN_UID = MY_ADMIN_UID;
const KV_NAMESPACE = tgbot;
const LAST_USER_KEY = 'last_user';
const USER_MESSAGES_KEY_PREFIX = 'user_message_';
const ADMIN_RESPONSES_KEY_PREFIX = 'admin_response_';

addEventListener('fetch', event => {
const url = new URL(event.request.url);
if (url.pathname === WEBHOOK) {
event.respondWith(handleWebhook(event));
} else if (url.pathname === '/registerWebhook') {
event.respondWith(registerWebhook(event, url, WEBHOOK, SECRET));
} else if (url.pathname === '/unRegisterWebhook') {
event.respondWith(unRegisterWebhook(event));
} else {
event.respondWith(new Response('No handler for this request'));
}
});

async function handleWebhook(event) {
if (event.request.headers.get('X-Telegram-Bot-Api-Secret-Token') !== SECRET) {
return new Response('Unauthorized', { status: 403 });
}

const update = await event.request.json();
event.waitUntil(onUpdate(update));

return new Response('Ok');
}

async function onUpdate(update) {
if ('message' in update) {
await onMessage(update.message);
}
}

async function onMessage(message) {
const chatId = message.chat.id;
const userName = message.from.username ? `@${message.from.username}` : message.from.first_name;

if (chatId == ADMIN_UID) {
// 处理管理员消息
let userChatId;

if (message.reply_to_message) {
const repliedMessageId = message.reply_to_message.message_id;
userChatId = await KV_NAMESPACE.get(`admin_message_${repliedMessageId}`);
if (!userChatId) {
await sendPlainText(ADMIN_UID, '无法找到要回复的用户消息。');
return;
}
} else {
// 获取最后一个活跃的用户
userChatId = await KV_NAMESPACE.get(LAST_USER_KEY);
if (!userChatId) {
await sendPlainText(ADMIN_UID, '没有最近活跃的用户会话。');
return;
}
}

// 根据管理员发送的消息类型,转发给对应的用户
let responseText = '';
if (message.photo) {
const photo = message.photo[message.photo.length - 1];
await sendPhoto(userChatId, photo.file_id);
responseText = `管理员发送了一张图片: ${photo.file_id}`;
} else if (message.sticker) {
await sendSticker(userChatId, message.sticker.file_id);
responseText = `管理员发送了一张贴纸: ${message.sticker.file_id}`;
} else if (message.voice) {
await sendVoice(userChatId, message.voice.file_id);
responseText = `管理员发送了一条语音消息: ${message.voice.file_id}`;
} else if (message.document) {
await sendDocument(userChatId, message.document.file_id);
responseText = `管理员发送了一份文件: ${message.document.file_id}`;
} else if (message.video) {
await sendVideo(userChatId, message.video.file_id);
responseText = `管理员发送了一段视频: ${message.video.file_id}`;
} else if (message.location) {
await sendLocation(userChatId, message.location.latitude, message.location.longitude);
responseText = `管理员发送了一个位置: 纬度 ${message.location.latitude}, 经度 ${message.location.longitude}`;
} else {
const text = message.text || '收到一个非文本消息';
await sendPlainText(userChatId, text);
responseText = text;
}
await KV_NAMESPACE.put(`${ADMIN_RESPONSES_KEY_PREFIX}${userChatId}`, responseText);
} else {
// 处理用户消息
let userMessageText = '';
let response;
if (message.photo) {
const photo = message.photo[message.photo.length - 1];
userMessageText = `用户发送了一张图片: ${photo.file_id}`;
response = await sendPhoto(ADMIN_UID, photo.file_id, `来自用户 ${userName} 的图片`);
} else if (message.sticker) {
userMessageText = `用户发送了一张贴纸: ${message.sticker.file_id}`;
response = await sendSticker(ADMIN_UID, message.sticker.file_id);
} else if (message.voice) {
userMessageText = `用户发送了一条语音消息: ${message.voice.file_id}`;
response = await sendVoice(ADMIN_UID, message.voice.file_id);
} else if (message.document) {
userMessageText = `用户发送了一份文件: ${message.document.file_id}`;
response = await sendDocument(ADMIN_UID, message.document.file_id);
} else if (message.video) {
userMessageText = `用户发送了一段视频: ${message.video.file_id}`;
response = await sendVideo(ADMIN_UID, message.video.file_id);
} else if (message.location) {
userMessageText = `用户发送了一个位置: 纬度 ${message.location.latitude}, 经度 ${message.location.longitude}`;
response = await sendLocation(ADMIN_UID, message.location.latitude, message.location.longitude);
} else {
const text = message.text || '用户发送了非文本消息';
userMessageText = text;
response = await sendPlainText(ADMIN_UID, `来自用户 ${userName} 的消息:\n${text}`);
}
// 保存管理员消息ID与用户聊天ID的映射
if (response && response.result && response.result.message_id) {
const adminMessageId = response.result.message_id;
await KV_NAMESPACE.put(`admin_message_${adminMessageId}`, chatId.toString());
}
await KV_NAMESPACE.put(`${USER_MESSAGES_KEY_PREFIX}${chatId}`, userMessageText);
// 更新最后一个活跃的用户
await KV_NAMESPACE.put(LAST_USER_KEY, chatId.toString());
}
}

function apiUrl(methodName) {
return `https://api.telegram.org/bot${TOKEN}/${methodName}`;
}

async function sendPlainText(chatId, text) {
const response = await fetch(apiUrl('sendMessage'), {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ chat_id: chatId, text })
});
return response.json();
}

async function sendSticker(chatId, fileId) {
const response = await fetch(apiUrl('sendSticker'), {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ chat_id: chatId, sticker: fileId })
});
return response.json();
}

async function sendPhoto(chatId, fileId, caption = '') {
const response = await fetch(apiUrl('sendPhoto'), {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ chat_id: chatId, photo: fileId, caption })
});
return response.json();
}

async function sendVoice(chatId, fileId) {
const response = await fetch(apiUrl('sendVoice'), {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ chat_id: chatId, voice: fileId })
});
return response.json();
}

async function sendDocument(chatId, fileId) {
const response = await fetch(apiUrl('sendDocument'), {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ chat_id: chatId, document: fileId })
});
return response.json();
}

async function sendVideo(chatId, fileId) {
const response = await fetch(apiUrl('sendVideo'), {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ chat_id: chatId, video: fileId })
});
return response.json();
}

async function sendLocation(chatId, latitude, longitude) {
const response = await fetch(apiUrl('sendLocation'), {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ chat_id: chatId, latitude, longitude })
});
return response.json();
}

async function registerWebhook(event, requestUrl, suffix, secret) {
const webhookUrl = `${requestUrl.protocol}//${requestUrl.hostname}${suffix}`;
const response = await fetch(apiUrl('setWebhook'), {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ url: webhookUrl, secret_token: secret })
});
const r = await response.json();
return new Response('ok' in r && r.ok ? 'Ok' : JSON.stringify(r, null, 2));
}

async function unRegisterWebhook(event) {
const response = await fetch(apiUrl('setWebhook'), {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ url: '' })
});
const r = await response.json();
return new Response('ok' in r && r.ok ? 'Ok' : JSON.stringify(r, null, 2));
}
  1. 替换掉默认代码后,右上角点击部署,然后顶部中间点击 worker.dev进行访问

  2. 完成bot部署
    url后面加上/registerWebhook,进行 webhook注册 (unRegisterWebhoo 取消注册)
    备注:如果打不开url就需要翻墙,因为在中国大陆cloudflare worker.dev域名被禁了,也可以给 worker绑定一个域名解决